package fr.jade.resources.joram.wrapper;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.util.Properties;
import java.util.Set;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.util.Fractal;
import org.objectweb.joram.client.jms.admin.AdminModule;

import fr.dyade.aaa.agent.AgentServer;
import fr.jade.fraclite.InitializationContext;
import fr.jade.fraclite.api.control.GenericAttributeController;
import fr.jade.reflex.api.control.ReflexController;
import fr.jade.reflex.control.lifecycle.ReflexLifeCycleController;
import fr.jade.reflex.util.Reflex;
import fr.jade.util.CollectionUtil;
import fr.jade.util.FractalNavigator;
import fr.jade.util.FractalUtil;

/**
 * 
 * @author Florent Metral
 * Life cycle controller for a Joram server
 */
public class JoramServerLifeCycleController extends ReflexLifeCycleController {

	private String hostName;

	private String a3File;

	private boolean root;

	private final String SERVER_PREFIX = "JoramServer_";

	private final int PORT_NUMBER_START = 16020;

	private final String STORAGE_DIR_PATH_PREFIX = "/tmp/S";

	protected Component weaveableC;
	
	
	@Override
	public void initFcController(InitializationContext ic) throws InstantiationException {
    super.initFcController(ic);
    weaveableC = (Component) ic.getInterface("component");
  }

  /**
	 * Life cycle flag
	 */
	private boolean started = false;

	private String getAttribute(String attName) {
		try {
			GenericAttributeController gac = FractalUtil.getAttributeController(weaveableC);
			return gac.getAttribute(attName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private void writeConfigFileBeginning() {
		try {
			boolean persistence = Boolean.parseBoolean(getAttribute("persistence"));
			String transaction = "";
			if(persistence){
				transaction = "fr.dyade.aaa.util.NTransaction";
			}
			else{
				transaction = "fr.dyade.aaa.util.NullTransaction";
			}
			a3File = "<?xml version=\"1.0\"?>\r\n";
			a3File = a3File + "<config>\r\n";
			a3File = a3File + "  <property name=\"Transaction\" value=\"" + transaction + "\"/>\r\n";
			a3File = a3File + "\r\n";
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void writeConfigFileEnding(){
		a3File = a3File + "</config>";
	}

	private boolean isFirstServer(){
		return (getGatewayServer() == null);
	}

	private Component getGatewayServer() {
//		FScriptInterpreter fscript = new FScriptInterpreter();
//		Node n = fscript.createComponentNode(_this_weaveableC);
//		Map<String, Object> vars = new HashMap<String, Object>();
//		vars.put("self", n);
//
//		try {
//			Set<Node> nodes = (Set<Node>) fscript.evaluate("$self/sibling::*[starts-with(name(.), 'JoramServer_')]", vars);
//			for (Node node : nodes) {
//				try {
//					ComponentNodeImpl cnode = (ComponentNodeImpl) node;
//					if(!Fractal.getNameController(cnode.getComponent()).getFcName().equals(SERVER_PREFIX + getAttribute("sid"))){
//						return cnode.getComponent();
//					}
//				} catch (NoSuchInterfaceException e) {
//					e.printStackTrace();
//				}
//			}
//			return null;
//		} catch (FScriptException e) {
//			e.printStackTrace();
//			return null;
//		}
		
		Set<Component> sibling = FractalNavigator.getSibling(weaveableC);
		Set<Component> servers = FractalNavigator.nameStartWithFilter(sibling, "JoramServer_");
		
		if(servers.isEmpty()){
			System.out.println("First server");
			return null;
		}else{
			System.out.println("not first server");
			return (Component)CollectionUtil.checkAndGetSingleton(servers);
		}
		
//		return servers.isEmpty()?null:servers.get(0);
	}

	private void writea3serverxml() {
		if(!isFirstServer()){
			root = false;
			int comPortInDomain = 17000 + Integer.parseInt(getAttribute("sid"));
			System.out.println("We have to update the admin module config to add this new server");
			try {
				Component rootServer = getGatewayServer();

				Properties gatewayServerConfig = new Properties();
				System.out.println(Fractal.getNameController(rootServer).getFcName() + " is chose as root");	
				int gwServerPort = PORT_NUMBER_START + Integer.parseInt(FractalUtil.getAttributeController(rootServer).getAttribute("sid"));

				gatewayServerConfig.put("hostName", FractalUtil.getAttributeController(FractalNavigator.getNode(rootServer)).getAttribute("hostName"));
				gatewayServerConfig.put("port", gwServerPort);
				gatewayServerConfig.put("sid",FractalUtil.getAttributeController(rootServer).getAttribute("sid"));
				gatewayServerConfig.put("userAdmin", FractalUtil.getAttributeController(rootServer).getAttribute("userAdmin"));
				gatewayServerConfig.put("passAdmin", FractalUtil.getAttributeController(rootServer).getAttribute("passAdmin"));
				System.out.println(gatewayServerConfig);

				AdminModule.connect(gatewayServerConfig.getProperty("hostName"), gwServerPort, gatewayServerConfig.getProperty("userAdmin"), gatewayServerConfig.getProperty("passAdmin"), 10);		

				String services[];
				String args[];
				Integer port = PORT_NUMBER_START + Integer.parseInt(getAttribute("sid"));
				services = new String[] {"org.objectweb.joram.mom.proxies.tcp.TcpProxyService"};
				args = new String[] {port.toString()};
				AdminModule.addServer(Integer.parseInt(getAttribute("sid")),
						hostName, 
						getAttribute("domain"),
						comPortInDomain,
						"S"+ getAttribute("sid"),
						services,
						args);
				System.out.println(AdminModule.getConfiguration());
				updatea3serverxmlFile(AdminModule.getConfiguration());
				AdminModule.disconnect();
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		else{
			try{
				root = true;
//				OutputStream fout = new FileOutputStream("a3servers.xml");
//				/*String cfgFile = System.getProperty(AgentServer.CFG_FILE_PROPERTY, AgentServer.DEFAULT_CFG_FILE);
//			OutputStream fout = new FileOutputStream(Activator.getBundleContext().getDataFile(cfgFile));*/
//				OutputStream bout = new BufferedOutputStream(fout);
//				OutputStreamWriter configWriter = new OutputStreamWriter(bout);
				writeConfigFileBeginning();
				a3File = a3File + printServer();
				writeConfigFileEnding();
				updatea3serverxmlFile(a3File);
//				configWriter.write(a3File);
//				configWriter.flush();
//				configWriter.close();
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private String printServer(){
		String result = "";

		String sid = getAttribute("sid");
		String userAdmin = getAttribute("userAdmin");
		int port = PORT_NUMBER_START + Integer.parseInt(getAttribute("sid"));

		result = result.concat("  <server id=\"" + sid + "\" name=\""
				+ "S" + getAttribute("sid") + "\" hostname=\""
				+ hostName + "\">\r\n");
		result = result.concat("    <service class=\"org.objectweb.joram.mom.proxies.ConnectionManager\"\r\n");
		result = result.concat("             args=\"" + userAdmin
				+ " " + getAttribute("passAdmin") + "\"/>\r\n");
		result = result.concat("    <service class=\"org.objectweb.joram.mom.proxies.tcp.TcpProxyService\"\r\n");
		result = result.concat("             args=\"" + port
				+ "\"/>\r\n");
		result = result.concat("  </server>\r\n\r\n");
		return result;
	}

	@Override
	public String getFcState() {
		return super.getFcState();
	}

	@Override
	public void startFc() throws IllegalLifeCycleException {
	  if(!started){
	    if(Reflex.isEL(weaveableC)){
    		try {
    			System.out.println("StartFc from Joram server wrapper called ...");
    			// Get our hostname
    			hostName = InetAddress.getLocalHost().getCanonicalHostName();
    
    			// Change our internal name
    			Fractal.getNameController(weaveableC).setFcName(SERVER_PREFIX + getAttribute("sid"));
    
    			// Generate a3servers.xml
    			writea3serverxml();
    			// Set storage dir
    			String storageDirName = STORAGE_DIR_PATH_PREFIX + getAttribute("sid");
    			File storageDir = new File(storageDirName);
    			File lockFile = new File(storageDir, "lock");
    			lockFile.delete();
    
    			// Start the underlying Joram server
    			System.out.println("Joram Server sid:" + getAttribute("sid") + " starting...");
    			AgentServer.init(new String[] {getAttribute("sid"), storageDir.getAbsolutePath() });
    			AgentServer.start();
    			System.out.println("Joram Server sid:" + getAttribute("sid") + " [started]");
    			started = true;
    
    			// Print the configuration. Useful for debugging
    			System.out.println("AdminModule.connect("+hostName + "," 
    					+ PORT_NUMBER_START  + "," 
    					+  Integer.parseInt(getAttribute("sid")) + "," 
    					+ getAttribute("userAdmin") + "," 
    					+ getAttribute("passAdmin") + ","
    					+ "10)");
    			AdminModule.connect(hostName, PORT_NUMBER_START + Integer.parseInt(getAttribute("sid")), getAttribute("userAdmin"), getAttribute("passAdmin"), 10);
    			System.out.println(AdminModule.getConfiguration());
    
    		} catch (Exception e){
    			e.printStackTrace();
    		}
    		if(root){
    			System.out.println("This server is the first created");
    			if(!getAttribute("domain").equals("")){
    				System.out.println("A domain has been specified - we have to update the admin module config");
    				try {
    					AdminModule.connect(hostName, PORT_NUMBER_START + Integer.parseInt(getAttribute("sid")), getAttribute("userAdmin"), getAttribute("passAdmin"), 10);		
    					AdminModule.addDomain(getAttribute("domain"), Integer.parseInt(getAttribute("sid")), 17000 + Integer.parseInt(getAttribute("sid")));
    					System.out.println(AdminModule.getConfiguration());
    					updatea3serverxmlFile(AdminModule.getConfiguration());
    					AdminModule.disconnect();
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		}
	    }
  		// calls the overriden method
  		super.startFc();
	  }
	}

	private void updatea3serverxmlFile(String config) {
		String cfgDir = System.getProperty(AgentServer.CFG_DIR_PROPERTY, AgentServer.DEFAULT_CFG_DIR);
		String cfgFile = System.getProperty(AgentServer.CFG_FILE_PROPERTY, AgentServer.DEFAULT_CFG_FILE);
		File f = new File(cfgDir, cfgFile);
		f.delete();
		try{
			OutputStream fout = new FileOutputStream("a3servers.xml");
			OutputStream bout = new BufferedOutputStream(fout);
			OutputStreamWriter configWriter = new OutputStreamWriter(bout);
			configWriter.write(config);
			configWriter.flush();
			configWriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void stopFc() throws IllegalLifeCycleException {
	  if(started){
	    if(Reflex.isEL(weaveableC)){
    		started = false;
    		try {
    		  hostName = InetAddress.getLocalHost().getCanonicalHostName();
          AdminModule.connect(hostName, PORT_NUMBER_START + Integer.parseInt(getAttribute("sid")), getAttribute("userAdmin"), getAttribute("passAdmin"), 10);   
          AdminModule.stopServer(Integer.parseInt(getAttribute("sid")));
          AdminModule.disconnect();
        } catch (Exception e) {
          e.printStackTrace();
        }
	    }
	    super.stopFc();
	  }
	}

}
